﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace Utilities
{
    public class EntityHelper<T> where T : class, new()
    {
        private static object _PROPERTYINFO_LOCK = new object();

        //private static readonly Type[] UserType = new Type[] { typeof(string), typeof(int), typeof(decimal), typeof(DateTime), typeof(Nullable<>), typeof(Nullable) };

        private static Dictionary<Type, PropertyInfo[]> _PropertyInfoCache = new Dictionary<Type, PropertyInfo[]>();


        public static T ConvertEntity(IDataReader reader)
        {
            T t = new T();

            foreach (PropertyInfo p in GetPropertyInfos(t.GetType()))
            {
                if (p.PropertyType == typeof(string) || p.PropertyType.IsValueType)
                    p.SetValue(t, GetDefaultValue(reader[p.Name], p.PropertyType), null);
            }
            return t;
        }

        public static List<T> ConvertEntityList(DataTable data)
        {
            return (from DataRow row in data.AsEnumerable() select ConvertEntity(row)).ToList();
        }


        public static List<T> ConvertEntityList(IEnumerable<DataRow> rows)
        {
            return (from DataRow row in rows select ConvertEntity(row)).ToList();
        }

        public static T ConvertEntity(DataRow row)
        {
            Type type = typeof(T);

            T target = new T();

            DataColumnCollection collectoin = row.Table.Columns;
            foreach (PropertyInfo p in GetPropertyInfos(type))
            {
                if ((p.PropertyType == typeof(string) || p.PropertyType.IsValueType) && collectoin.Contains(p.Name))
                    p.SetValue(target, GetDefaultValue(row[p.Name], p.PropertyType), null);
            }
            return (T)target;
        }

        public static T ConvertEntity(DataRowView row)
        {
            Type type = typeof(T);

            T target = new T();



            foreach (PropertyInfo p in GetPropertyInfos(type))
            {
                if (p.PropertyType == typeof(string) || p.PropertyType.IsValueType)
                    p.SetValue(target, GetDefaultValue(row[p.Name], p.PropertyType), null);
            }
            return (T)target;
        }

        public static void PropertiesEach(object model, Action<PropertyInfo, object> method)
        {
            if (model == null)
                return;

            Type type = typeof(T);

            foreach (PropertyInfo prop in GetPropertyInfos(type))
            {
                if (prop.PropertyType == typeof(string) || prop.PropertyType.IsValueType)
                {
                    object value = prop.GetValue(model, null);
                    method(prop, value);
                }
            }

        }


        public static object GetDefaultValue(object obj, Type type)
        {
            if (obj == DBNull.Value)
            {
                //if (!(type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>))))
                //    return System.Activator.CreateInstance(type);
                return null;
            }


            if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                type = System.Nullable.GetUnderlyingType(type);
            return Convert.ChangeType(obj, type);
        }


        public static PropertyInfo[] GetPropertyInfos(Type type)
        {
            PropertyInfo[] props = null;
            lock (_PROPERTYINFO_LOCK)
            {

                _PropertyInfoCache.TryGetValue(type, out props);
                if (props == null)
                {
                    props = type.GetProperties();
                    _PropertyInfoCache.Add(type, props);
                }
            }
            return props;
        }



    }
}
